const child_process = require('child_process');
const _ = require('lodash');
const moment = require('moment');
const fs = require('fs-extra');
const path = require('path');
const os = require('os');

function sleep(ms) {
  return new Promise(resolve => { setTimeout(resolve, ms); });
}
function debug(...args) {
  this.DEBUG && console.log(...args);
}
function logToFile(file, obj, isText) {
  fs.writeFileSync(file, isText ? obj : JSON.stringify(obj));
}
function logAppendFile(file, obj, isText) {
  fs.appendFileSync(file, isText ? obj : JSON.stringify(obj));
}
function saveHistory(file, line) {
  let list = fs.existsSync(file) ? fs.readFileSync(file, 'utf-8').split(/\n/) : [];
  list = _.reverse(list);
  list.unshift(line);
  list = _.uniq(list);
  list = _.reverse(list);
  fs.writeFileSync(file, list.join('\n').replaceAll('$', '\\$'), 'utf-8');
}
async function until(test, ms = 100) {
  while (!test()) {
    await sleep(ms);
  }
}
function omitNil(obj) {
  return _.omitBy(obj, o => _.isNil(o) || _.isNaN(o));
}
function _r(...reg) {
  return new RegExp(...reg);
}
function getSameString(str, str1) {
  let i = 0;
  while (str[i] && str[i] === str1[i]) {
    i++;
  }
  return { head: str.substr(0, i), tail: str.substr(i) };
}
function getFileText(path) {
  try {
    return fs.readFileSync(path, 'utf-8');
  } catch (e) {
    // console.log('eval error:', e.message);
    return '';
  }
}
function getFileJSON(path) {
  try {
    const str = fs.readFileSync(path, 'utf-8');
    return (new Function(`return ${str}`))();
  } catch (e) {
    // console.log('eval error:', e.message);
    return {};
  }
}
function parseJSON(str, defaultValue) {
  try {
    return JSON.parse(str);
  } catch (e) {
    console.log('json parse error:', e.message);
    return defaultValue;
  }
}
/*
function :直接返回为函数
字符串：
'=>123' :返回 123，可以使用 $, _, moment, utils, $$ 等符号
'=$123' :返回 input123 其中 $ 代表当前参数，直接替换
'return 123' :与 '=>123'相同，返回 123，可以使用 $, _, moment, utils, $$ 等符号
'($, {_, moment, utils, $$})=>123' :返回 123
'@1.js' :返回1.js中exports的函数
其他： 直接返回字符串
*/
function parseFunction(func, sync) {
  if (_.isString(func)) {
    const _func = func.trim();
    if (/^@.*/.test(_func)) {
      return loadModule(path.resolve(process.cwd(), _func));
    } else if (/^=>/.test(_func)) {
      return (new Function(`return ${sync ? '' : 'async'}($, { _, moment, utils, $$ } = {})${_func}`))();
    } else if (/^=/.test(_func)) {
      return (new Function(`return ${sync ? '' : 'async'}($)=>'${_func.slice(1)}'.replace(/\\$/g, $)`))();
    } else if (/=>/.test(_func)) {
      return (new Function(`return ${_func}`))();
    } else if (/^return /.test(_func)) {
      return (new Function(`return ${sync ? '' : 'async'}($, { _, moment, utils, $$ } = {})=>{ ${_func} }`))();
    }
  }
  return func;
}
function eval(str) {
  try {
    return { value: (new Function(`return ${str}`))() };
  } catch (e) {
    this.DEBUG && console.log('eval error:', e.message);
    return { error: e.message || 'evel错误', formula: str };
  }
}
function getIPAdress() {
  var interfaces = os.networkInterfaces();
  for (var devName in interfaces) {
    var iface = interfaces[devName];
    for (var i = 0; i < iface.length; i++) {
      var alias = iface[i];
      if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
        return alias.address;
      }
    }
  }
}
function loadModule(file) {
  const Module = module.constructor;
  const m = new Module();
  const code = fs.readFileSync(file, "utf8");
  m._compile(code, file);
  return m.exports;
}
let uuid_index = _.random(1000, 9999);
function uuid() {
  var s = [];
  var hexDigits = "0123456789abcdef";
  for (var i = 0; i < 9; i++) {
    s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
  }
  var uniq = uuid_index.toString(16);
  while (uniq.length < 4) uniq = `0${uniq}`;
  uuid_index++;
  return Date.now().toString(16) + s.join('') + uniq;
}
function checkIdNo(idNo) {
  if (!idNo) {
    return console.log('empty');
  }
  const number = idNo.split('');
  const W = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];// 加权因子
  const ValideCode = [1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2];// 身份证验证位值.10代表X
  if (!number || number.length !== 18) {
    return console.log('error idNo');
  }
  if (number[17] === 'x' || number[17] === 'X') {
    number[17] = 10;
  }
  let sum = 0;
  for (let i = 0; i < 17; i++) {
    sum += W[i] * number[i];
  }
  if (number[17] != ValideCode[sum % 11]) {
    return console.log(`[checkIdNo]: ${idNo} last number should be ${ValideCode[sum % 11]}`);
  }
  return console.log('success');
}
function getIPAdress() {
  var ip;
  var interfaces = os.networkInterfaces();
  for (var devName in interfaces) {
    var iface = interfaces[devName];
    for (var i = 0; i < iface.length; i++) {
      var alias = iface[i];
      if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
        ip = alias.address;
      }
    }
  }
  return ip;
}
function copy(str) {
  var command;
  switch (process.platform) {
    case 'darwin':
      command = 'echo "' + str.replace(/"/g, '\\"') + '"|pbcopy';
      break;
    case 'win32':
      return child_process.exec('clip').stdin.end(require('iconv-lite').encode(str, 'gbk'));
      // 一下这个方法只能复制很少的量，故舍弃
      // command = 'mshta vbscript:clipboarddata.setdata("text","'+str.replace(/"/g, '""')+'")(close)';
      break;
    default:
      throw new Error('Unknown platform: ' + process.platform);
  }
  child_process.exec(
    command,
    function (err, stdout, stderr) {
      if (err) {
        throw new Error('copy error');
      }
    }
  );
}
function paste(callback) {
  switch (process.platform) {
    case 'darwin':
      child_process.exec(
        'pbpaste',
        function (err, stdout, stderr) {
          if (err) {
            throw new Error('copy error');
          }
          callback(stdout);
        }
      );
      break;
    case 'win32':
      child_process.exec('uname',
        function (err, stdout, stderr) {
          if (!err) {
            return child_process.exec('cat /dev/clipboard',
              function (err, stdout, stderr) {
                if (err) {
                  console.log(err);
                  throw new Error("copy error");
                }
                callback(stdout);
              }
            );
          }
          var paste_vbs = 'Set objHTML = CreateObject("htmlfile")\n'
            + 'text = objHTML.ParentWindow.ClipboardData.GetData("Text")\n'
            + 'Wscript.Echo text';
          fs.writeFileSync('paste.vbs', paste_vbs);
          child_process.exec('cscript /nologo paste.vbs',
            function (err, stdout, stderr) {
              if (err) {
                console.log(err);
                throw new Error('copy error');
              }
              callback(stdout);
              fs.unlinkSync('paste.vbs');
            }
          );
        }
      );
      break;
    default:
      throw new Error('Unknown platform: ' + process.platform);
  }
}
function pasteSync() {
  return new Promise(resolve => {
    paste(resolve);
  });
}
function json_stringify(obj, pretty) {
  const str = JSON.stringify(obj, (key, value)=>{
      if (_.includes(['hash', 'salt', '__v'], key)) {
          return undefined;
      }
      if (value instanceof Array && _.every(value, o=>+o==o)) {
          return `__$__${JSON.stringify(value)}__$__`;
      }
      return value;
  }, pretty ? 2 : 0);
  return str.replace(/"__\$__\[/g, '[').replace(/]__\$__"/g, ']').replace(/\\"/g, '"');
}
// type: 0: js 1: json 2:inline-js 3:inline-json
function stringify(obj, type = 0, pad = 2, space = ' ', bk = os.EOL, level = 0) {
  if (type === 2) {
    return stringify(obj, 0, 0, '', '');
  }
  if (type === 3) {
    return JSON.stringify(obj);
  }
  const keyAuot = type === 0 ? `` : `"`;
  const valueAuot = type === 0 ? `'` : `"`;
  if (_.isPlainObject(obj)) {
    const line = [`{`];
    const keys = _.keys(obj);
    for (const i in keys) {
      line.push(`${_.repeat(space, (level + 1) * pad)}${keyAuot}${keys[i]}${keyAuot}: ${stringify(obj[keys[i]], type, pad, space, bk, level + 1)}${i == keys.length - 1 ? '' : ','}`);
    }
    line.push(`${_.repeat(space, level * pad)}}`);
    return line.join(bk);
  } else if (_.isArray(obj)) {
    const line = [];
    for (const o of obj) {
      if (!_.isPlainObject(o) && !_.isArray(o)) {
        line.push(_.isString(o) ? `${valueAuot}${_r(valueAuot).test(o) ? o.trim().replace(_r(valueAuot, 'g'), `\\${valueAuot}`) : o.trim()}${valueAuot}` : `${o}`);
      } else {
        line.push(`${stringify(o, type, pad, space, bk, level)}`);
      }
    }
    return `[${line.join(',')}]`;
  } else if (_.isString(obj)) {
    return `${valueAuot}${obj.trim()}${valueAuot}`;
  }
  return obj;
}
function stringifyWeb(obj, type = 0, pad = 2) {
  return stringify(obj, type, pad, '&nbsp', '<br>');
}
function md5(text) {
  const hash = require('crypto').createHash('md5');
  hash.update(text);
  return hash.digest('hex');
}
function httpPost(url, data = {}) {
  console.log('[httpPost] send:', url, data);
  return new Promise(resolve => {
    require('superagent').post(url)
      .set({ 'Content-Type': `application/json;charset=UTF-8` })
      .send(data)
      .end((error, res) => {
        if (!error && res.statusCode === 200) {
          console.log('[httpPost] receive:', url, res.body);
          resolve(res.body);
        } else {
          resolve();
        }
      });
  });
}
function httpGet(url, data = {}) {
  console.log('[httpGet] send:', url, data);
  return new Promise(resolve => {
    require('superagent').get(url)
      .query(data)
      .end((error, res) => {
        if (!error && res.statusCode === 200) {
          if (res.text) {
            console.log('[httpPost] receive:', url, res.text);
            resolve(res.text);
          } else if (_.size(res.body)) {
            console.log('[httpPost] receive:', url, res.body);
            resolve(res.body);
          } else {
            resolve();
          }
        } else {
          resolve();
        }
      });
  });
}
function timeConversion(timelong) {
  if (timelong) {
    const ss = parseInt((timelong % 3600) % 60);
    const mm = parseInt((timelong % 3600) / 60);
    const hh = parseInt(timelong / 3600);
    if (timelong < 60) {
      return (ss + '秒');
    } else if (timelong < 3600) {
      return mm ? (mm + '分' + ss + '秒') : (ss + '秒');
    } else {
      return !ss && !mm ? (hh + '小时') : !ss ? (hh + '小时' + mm + '分') : (hh + '小时' + mm + '分' + ss + '秒');
    }
  } else {
    return ('0秒');
  }
}
function removeHtmlTag(html, tag) {
  html = html || '';
  return html.replace(new RegExp(`</?span.*?>`, 'g'), '');
}
//rgba(31,147,255,0.73)
//rgb(31,147,255)
function rgbaToHex(rgba) {
  var list = rgba.replace(/\s*/g, '').match(/rgba?\((\d+),(\d+),(\d+),?(.*)?\)/);
  if (list) {
    return `#${(+list[1]).toString(16).padStart(2, '0')}${(+list[2]).toString(16).padStart(2, '0')}${(+list[3]).toString(16).padStart(2, '0')}${((list[4] || 1) * 100).toString(16).padStart(2, '0')}`.toUpperCase();
  }
  return '';
}
//#1F93FF49
function hexToRgba(hex) {
  const list = hex.match(/#(..)(..)(..)(..)?/);
  if (list) {
    return `rgba(${parseInt(list[1], 16)},${parseInt(list[2], 16)},${parseInt(list[3], 16)},${parseInt(list[4] || '64', 16) / 100})`;
  }
  return '';
}
/*
* 简化JSON
* 规则：
* 1、基本单元项必须为m=n的形式，中间不能有空格, 如果值需要空格，需要由\转义
* 2、如果是数字，默认为数字，添加了引号的为字符串，如果不是数字，默认不加引号表示字符串，添加了引号则引号为字符串的一部分
* 3、函数必须为o=>xxx 或 o=>{...} 或 (o,m)=>xxx 或(o,m)=>{...}的格式 (中间不能有空格)
* 4、子Object为x={}的形式，子Array为x=[]的形式，所有分隔符都是空格
*/

// 示例：
// const str = `a=1 b=-3    c='1'    d='方运江'`;
// -> { a: 1, b: -3, c: '1', d: '\'方运江\'' }
// const str = `d1=false d2=true d3='true' d4 !d5`;
// -> { d1: false, d2: true, d3: 'true', d4: true, d5: false }
// const str = `m=[1 2 3]`;
// -> { m: [ 1, 2, 3 ] }
// const str = `f={fang a=1 f={fang a=1 }}`;
// -> { f: { fang: true, a: 1, f: { fang: true, a: 1 } } }
// const str = `d=a\\ b\\ c f=p g={a=1 b=-3 g={a=1 b=-3}}`;
// -> { d: 'a b c', f: 'p', g: { a: 1, b: -3, g: { a: 1, b: -3 } } }
// const str = `d=a\\ b\\ c f=/^g/`;
// -> { d: 'a b c', f: /^g/ }
// const str = `d=a\\ b\\ c f=o=>o f1=o=>{return o+1} f2=(a,b)=>a+b f3=(a,b)=>{return a+b;}`;
// -> { d: 'a b c',
//      f: '__function_start__o=>o____function_end__',
//      f1: '__function_start__o=>{return o+1}____function_end__',
//      f2: '__function_start__(a,b)=>a+b____function_end__',
//      f3: '__function_start__(a,b)=>{return a+b;}____function_end__' }
// const str = 'm=[1 2\\ 5 3 [ 1 2 3 ] {fang=fang a=[1 2]}]';
// -> { m: [ 1, '2 5', 3, [ 1, 2, 3 ], { fang: 'fang', a: [1, 2] } ] }
// const str = `m=[1 2 {m=1 n=2} {m=[1,2]}]`;
// -> { m: [ 1, 2, { m: 1, n: 2 }, { m: [Array] } ] }
// console.log(parseObject(str));

function parseObject(line) {
  line = line.replace(/\s*=\s*/g, '=').replace(/\s*=>\s*/g, '=>');
  const json = {};
  const list = [];
  let i = -1, max = line.length - 1, item = '', flag = 0;
  let amatch = 0, bmatch = 0, cmatch = 0;
  while (i++ < max) {
    const ch = line[i];
    if (ch === '\\') {
      flag = 1;
      if (amatch || bmatch) {
        item = `${item}${ch}`;
      }
      continue;
    }
    if (ch === '{') {
      amatch++;
    } else if (ch === '}') {
      amatch--;
    } else if (ch === '[') {
      bmatch++;
    } else if (ch === ']') {
      bmatch--;
    } else if (ch === '(') {
      cmatch++;
    } else if (ch === ')') {
      cmatch--;
    }
    if (ch === ' ' && flag !== 1 && amatch === 0 && bmatch === 0 && cmatch === 0) {
      item && list.push(item);
      item = '';
    } else {
      item = `${item}${ch}`;
    }
    flag = 0;
  }
  if (amatch === 0 && bmatch === 0 && cmatch === 0) { // 如果括号不匹配，则丢弃
    item && list.push(item);
  }

  list.forEach(o => {
    const split = o.indexOf('=');
    let val, key;
    if (split === -1) {
      key = o;
    } else {
      key = o.substr(0, split);
      val = o.substr(split + 1);
    }

    if (val === undefined) {
      if (key[0] === '!') {
        key = key.slice(1);
        val = false;
      } else {
        val = true;
      }
    } else if (val === 'true') {
      val = true;
    } else if (val === 'false') {
      val = false;
    } else if (/^-?\d+(\.\d+)?$/.test(val)) { //数字
      val = val * 1;
    } else if (/^'(-?\d+(\.\d+)?)|true|false'$/.test(val)) {
      val = val.replace(/^'|'$/g, '');
    } else if (val[0] === '{') { // 子json
      val = parseObject(val.substring(1, val.length - 1));
    } else if (val[0] === '[') { // 子jsonArray
      val = parseArray(val.substring(1, val.length - 1));
    } else if (/^\/[^/]+\/$/.test(val)) { // 正则表达式
      val = new RegExp(val.substring(1, val.length - 1));
    } else if (/=>/.test(val)) { // 函数
      val = `__function_start__${val}____function_end__`;
    }
    Object.assign(json, { [key]: val });
  });
  return json;
}
function parseArray(line) {
  line = line.replace(/\s*=\s*/g, '=').replace(/\s*=>\s*/g, '=>');
  const list = [];
  let i = -1, max = line.length - 1, item = '', flag = 0;
  let amatch = 0, bmatch = 0, cmatch = 0;
  while (i++ < max) {
    const ch = line[i];
    if (ch === '\\') {
      flag = 1;
      if (amatch || bmatch) {
        item = `${item}${ch}`;
      }
      continue;
    }
    if (ch === '{') {
      amatch++;
    } else if (ch === '}') {
      amatch--;
    } else if (ch === '[') {
      bmatch++;
    } else if (ch === ']') {
      bmatch--;
    } else if (ch === '(') {
      cmatch++;
    } else if (ch === ')') {
      cmatch--;
    }
    if (ch === ' ' && flag !== 1 && amatch === 0 && bmatch === 0 && cmatch === 0) {
      item && list.push(item);
      item = '';
    } else {
      item = `${item}${ch}`;
    }
    flag = 0;
  }
  if (amatch === 0 && bmatch === 0 && cmatch === 0) { // 如果括号不匹配，则丢弃
    item && list.push(item);
  }
  return list.map(val => {
    if (val === 'true') {
      val = true;
    } else if (val === 'false') {
      val = false;
    } else if (/^-?\d+(\.\d+)?$/.test(val)) { //数字
      val = val * 1;
    } else if (/^'(-?\d+(\.\d+)?)|true|false'$/.test(val)) {
      val = val.replace(/^'|'$/g, '');
    } else if (val[0] === '{') { // 子json
      val = parseObject(val.substring(1, val.length - 1));
    } else if (val[0] === '[') { // 子jsonArray
      val = parseArray(val.substring(1, val.length - 1));
    } else if (/^\/[^/]+\/$/.test(val)) { // 正则表达式
      val = new RegExp(val.substring(1, val.length - 1));
    } else if (/=>/.test(val)) { // 函数
      val = `__function_start__${val}____function_end__`;
    }
    return val;
  });
}
// const str = { m: [ 1, 2, { m: 1, n: 2 }, { m: [1, 2] } ] }
// ->  `m=[1 2 {m=1 n=2} {m=[1,2]}]`;
// 这个函数是 parseObject 的逆向
function formatObject(value, child) {
  if (typeof (value) === 'string') {
    if (/^__function_start__.*__function_end__$/.test(value)) {
      return value.replace(/__function_start__|____function_end__/g, '');
    }
    if (/^-?\d+(\.\d+)?$/.test(value)) {
      return `'${value}'`;
    }
    if (/ /.test(value)) {
      return value.replace(/ /g, '\\\\ ');
    }
    if (value === 'true') {
      return `'${value}'`;
    }
    if (value === 'false') {
      return `'${value}'`;
    }
  }
  if (typeof (value) !== 'object') {
    return value;
  }
  if (value instanceof RegExp) {
    return value;
  }
  if (value instanceof Array) {
    return `[${value.map(o => formatObject(o, 1)).join(' ')}]`;
  }
  const str = `${_.map(value, (o, k) => `${k}=${formatObject(o, 1)}`).join(' ')}`;
  return child ? `{${str}}` : str;
}
// 解析 ' 和 " 的情况
// 如： 12 '1 2' '1\'2' "1\"2" "1'2" '1"2' -> [12, '1 2', "1'2", '1"2', "1'2", '1"2']
function parseLine(line) {
  const list = [];
  let i = -1, max = line.length - 1, item = '', flag = 0;
  let amatch = 0, bmatch = 0;
  while (i++ < max) {
    const ch = line[i];
    if (ch === '\\') {
      flag = 1;
      continue;
    }
    if (ch === "'" && !flag && !bmatch) {
      if (!amatch) {
        amatch = 1;
      } else {
        list.push(item);
        amatch = 0;
        item = '';
      }
      continue;
    }
    if (ch === '"' && !flag && !amatch) {
      if (!bmatch) {
        bmatch = 1;
      } else {
        list.push(item);
        bmatch = 0;
        item = '';
      }
      continue;
    }
    if (ch === ' ' && !flag && !amatch && !bmatch) {
      item && list.push(item);
      item = '';
    } else if (ch === ' ' && !item) {
      item = '';
    } else {
      item = `${item}${ch}`;
    }
    flag = 0;
  }
  item && list.push(item);
  return list;
}
// 获取到成对的标识符之间的内容
function getBlockLine(line, head, tail) {
  let i = -1, max = line.length - 1, item = '';
  let amatch = 0, escape = 0, ins = 0, inS = 0, result;
  while (i++ < max) {
    const ch = line[i];
    if (ch === '\\') {
      escape = 1;
      item = `${item}${ch}`;
      continue;
    }
    if (ch === head && !escape && !ins && !inS) {
      amatch++;
    } else if (ch === tail && !escape && !ins && !inS) {
      amatch--;
      if (!amatch) {
        result = `${item}${ch}`;
        break;
      }
    } else if (ch === `'` && !escape && !inS) {
      ins = !ins;
    } else if (ch === `"` && !escape && !ins) {
      inS = !inS;
    }
    item = `${item}${ch}`;
    escape = 0;
  }
  !result && (result = item);
  if (_.last(result) !== tail) {
    throw new Error(`表达式：${result}的${head}和${tail}不匹配`);
  }
  // 检查块代码后面有没有直接跟变量
  const left = _.trimStart(line.slice(i + 1));
  if (/^\w/.test(left)) {
    throw new Error(`表达式：${left.slice(0, 20)} 前缺少,`);
  }
  return result;
}
// 解析行删除注释
function deleteComment(line) { // 去除注释，判断括号的数量
  line = line.trim();
  let i = -1, max = line.length - 1, item = '';
  let amatch = 0, bmatch = 0, cmatch = 0, escape = 0, isComment = 0, isBlockComment = 0, ins = 0, inS = 0;
  while (i++ < max) {
    const ch = line[i];
    if (ch === '\n' || ch === '\r') {
      isComment = 0; // 新的一行后注释清零
      item = `${item}${ch}`;
      continue;
    }
    if (isComment) {
      continue;
    }
    if (ch === '\\') {
      escape = 1;
      item = `${item}${ch}`;
      continue;
    }
    if (!escape && !ins && !inS && ch === '/' && line[i + 1] === '/') { // 判断是否是注释
      isComment = 1;
      i++;
      continue;
    }
    if (!escape && !ins && !inS && !isBlockComment && !isComment && ch === '/' && line[i + 1] === '*') { // 判断是否是块注释开头
      isBlockComment = 1;
      i++;
      continue;
    }
    if (isBlockComment) { // 块注释跳过
      if (ch === '*' && line[i + 1] === '/') { // 判断是否是块注释结尾
        isBlockComment = 0;
        i++;
      }
      continue;
    }
    if (ch === '(' && !escape && !ins && !inS) {
      amatch++;
    } else if (ch === ')' && !escape && !ins && !inS) {
      amatch--;
    } else if (ch === '{' && !escape && !ins && !inS) {
      bmatch++;
    } else if (ch === '}' && !escape && !ins && !inS) {
      bmatch--;
    } else if (ch === '[' && !escape && !ins && !inS) {
      cmatch++;
    } else if (ch === ']' && !escape && !ins && !inS) {
      cmatch--;
    } else if (ch === `'` && !escape && !inS) {
      ins = !ins;
    } else if (ch === `"` && !escape && !ins) {
      inS = !inS;
    }
    item = `${item}${ch}`;
    escape = 0;
  }
  if (amatch) {
    throw new Error('()不匹配');
  }
  if (bmatch) {
    throw new Error('{}不匹配');
  }
  if (cmatch) {
    throw new Error('[]不匹配');
  }
  item = item.trim();
  if (item[0] === '{' && _.last(item) !== '}') {
    throw new Error('{}不匹配');
  }
  if (item[0] === '[' && _.last(item) !== ']') {
    throw new Error('[]不匹配');
  }
  if (item[0] === '(' && _.last(item) !== ')') {
    throw new Error('()不匹配');
  }
  return item;
}
function parseUrl(url) {
  const params = {}
  if (url.indexOf('?') < 0) return { url, params };
  const arr = url.split('?');
  const list = arr[1].split('&');
  for (const item of list) {
    const items = item.split('=');
    params[items[0]] = items[1] || true;
  }
  return { url: arr[0], params };
}
// 名称和路径不能带空格和*
async function downloadImage(url, fullname) {
  fullname = fullname.replace(/\s+/g, '').replace(/[*]/g, 'x');
  return new Promise(resolve => {
    if (!/^https?:\/\//.test(url)) {
      return resolve();
    }
    fs.ensureDirSync(path.dirname(fullname));
    require('request')(url).pipe(fs.createWriteStream(fullname)).on('close', function () {
      resolve(fullname);
    }).on('error', function (e) {
      console.log(`下载失败: `, url);
      resolve();
    });
  });
}
/*
可以输入rs重置路由的express的router，参数为文件夹或者文件
const app = express();
app.use(utils.reloadRouter('./router')); // 重新加载
app.listen(4000, function () {
  console.log("server listen on: http://localhost:4000");
});
*/
function reloadRouter(target) {
  let folder = require.resolve(target);
  if (fs.statSync(folder).isFile()) {
    folder = path.dirname(folder);
  }
  process.stdin.on('data', line => { // 输入rs后重启
    line = line.toString().trim();
    if (line === 'rs') {
      Object.keys(require.cache).forEach(function (id) {
        if (id.startsWith(folder)) {
          console.log('清除缓存:', id);
          delete require.cache[id];
        }
      });
      console.log('路由已重启');
    }
  });
  return function (req, res, next) {
    require(target)(req, res, next);
  }
}
module.exports = {
  _,
  moment,
  date: o => moment(o).format('YYYY-MM-DD'),
  time: o => moment(o).format('YYYY-MM-DD HH:mm:ss'),
  fs,
  path,
  os,
  DEBUG: false,
  debug,
  logToFile,
  logAppendFile,
  saveHistory,
  sleep,
  _r,
  getSameString,
  until,
  getIPAdress,
  loadModule,
  parseFunction,
  eval,
  parseJSON,
  getFileText,
  getFileJSON,
  copy,
  paste,
  pasteSync,
  omitNil,
  getIPAdress,
  uuid,
  checkIdNo,
  json_stringify,
  stringify,
  stringifyWeb,
  md5,
  httpPost,
  httpGet,
  timeConversion,
  removeHtmlTag,
  rgbaToHex,
  hexToRgba,
  parseObject: parseArray,  // `a=1 b=-3    c='1'    d='方运江'` -> { a: 1, b: -3, c: '1', d: '\'方运江\'' }
  formatObject, // { m: [ 1, 2, { m: 1, n: 2 }, { m: [1, 2] } ] } -> `m=[1 2 {m=1 n=2} {m=[1,2]}]`
  parseLine, // 如： 12 '1 2' '1\'2' "1\"2" "1'2" '1"2' -> [12, '1 2', "1'2", '1"2', "1'2", '1"2']
  deleteComment, // 解析行删除行注释和块
  getBlockLine, // 获取到成对的标识符(如())之间的内容-> (1,2,3)
  parseUrl,
  downloadImage,
  reloadRouter,
}
